﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO;
using System.Windows.Threading;
using System.Collections.Specialized;
using DirectoryViewModels;
using System.Threading;
using BrainTechLLC.FileMaintenance;
using DragDropLibrary;
using BrainTechLLC.WPF;


namespace BrainTechLLC.FolderExplorer
{
    public partial class FolderOperationHandler : IFolderOperationHandler
    {
        public bool _done = true;
        public event EventHandler<ObjectEventArgs<FolderViewModel>> FindFoldersToDeleteStart;
        public event EventHandler<ObjectEventArgs<FolderViewModel>> FindFoldersToDeleteEnd;
        public event EventHandler<ObjectEventArgs<FolderViewModel>> SelectFoldersStart;
        public event EventHandler<ObjectEventArgs<FolderViewModel>> SelectFoldersEnd;

        public virtual void SelectFoldersByCriteria(FolderViewModel folderSelected, Predicate<FolderViewModel> selectionCriteria)
        {
            FolderViewModel root = folderSelected.FindRoot();
            root.AddOperation("SelectFoldersByCriteria");
            ParameterizedThreadStart ts = new ParameterizedThreadStart(DoSearchWorker);
            Thread t = new Thread(ts) { Priority = ThreadPriority.BelowNormal, IsBackground = true };
            FolderAndSelectionCriteria criteria = new FolderAndSelectionCriteria() { FolderSelected = folderSelected, SelectionCriteria = selectionCriteria };
            t.Start(criteria);
        }

        public virtual void SelectFolders(FolderAndSelectionCriteria item)
        {
            FolderViewModel folderSelected = item.FolderSelected;
            Predicate<FolderViewModel> selectionCriteria = item.SelectionCriteria;

            SelectFoldersStart.RaiseEvent(this, folderSelected);

            BreadthFirstSearch search = new BreadthFirstSearch();
            Dictionary<string, FolderViewModel> folderList = new Dictionary<string, FolderViewModel>();
            List<FolderViewModel> foldersToSelect = new List<FolderViewModel>();
            _done = false;            

            var found = search.FindChild(folderSelected,
            (folder) =>
            {
                if (selectionCriteria(folder))
                {
                    foldersToSelect.Add(folder);
                    //FolderExplorerControl._Dispatcher.BeginInvoke(new VoidDelegate(() => { folder.IsChecked = true; folder.IsExpanded = true; }), DispatcherPriority.Background);
                }
                return false;
            },
            (folder) => folder.FullPath.StartsWith(folderSelected.FullPath), 0, 10,
            (folders) => _done = true, ref folderList);

            int foldersCount = foldersToSelect.Count;

            FolderExplorerControl._Dispatcher.BeginInvoke(new VoidDelegate(() =>
            {
                for (int n = 0; n < foldersToSelect.Count; n++)
                {
                    var folder = foldersToSelect[n];
                    folder.IsChecked = true;
                    folder.IsExpanded = true;
                    foldersCount--;
                }
            }), DispatcherPriority.Background);

            bool raisedEnd = false;

            SimpleTimer timer = new SimpleTimer(200, 200, (tmr) =>
            {
                if (_done && foldersCount == 0 && !raisedEnd)
                {
                    raisedEnd = true;
                    SelectFoldersEnd.RaiseEvent(this, folderSelected);
                    return RunInitializationResult.StopTimer;
                }
                return RunInitializationResult.DidntDoAnything;
            });

            FolderViewModel root = folderSelected.FindRoot();
            root.RemoveOperation("SelectFoldersByCriteria");

            while (timer._timer != null)
            {
                Thread.Sleep(50);
            }
        }

        private void DoSearchWorker(object folderSelectedObject)
        {
            FolderAndSelectionCriteria item = folderSelectedObject as FolderAndSelectionCriteria;
            SelectFolders(item);
        }

        public virtual void FindFoldersToDeleteAsync(FolderViewModel f)
        {
            FolderViewModel root = f.FindRoot();
            root.AddOperation("FindFoldersToDelete");
            ParameterizedThreadStart ts = new ParameterizedThreadStart(FindFoldersToDeleteWorkerThread);
            Thread t = new Thread(ts) { IsBackground = true, Priority = ThreadPriority.BelowNormal };
            t.Start(f);
        }

        public virtual void FindFoldersToDelete(FolderViewModel f)
        {
            FindFoldersToDeleteStart.RaiseEvent(this, f);

            try
            {
                List<DirectoryInfo> dirs = new List<DirectoryInfo>();
                List<FileInfo> files = new List<FileInfo>();
                FileWorkerUtil fileWorker = new FileWorkerUtil();
                List<Exception> exceptionsOut = new List<Exception>();

                fileWorker.StartNukeBinObjs(new DirectoryInfo(f.FullPath), new List<string>(), exceptionsOut, 0, 0,
                    Options.RemoveReadOnlyFlags, (o) => Response.CapitalYes, (o) => Response.CapitalYes,
                    (ex) => Console.WriteLine(ex.ToString()), ref dirs, ref files);

                var dirNames = (from dir in dirs select dir.FullName).ToList();
                SelectFoldersByCriteria(f, (folder) => dirNames.Contains(folder.FullPath, StringComparer.OrdinalIgnoreCase));
            }
            finally
            {
                FindFoldersToDeleteEnd.RaiseEvent(this, f);
            }

            FolderViewModel root = f.FindRoot();
            root.RemoveOperation("FindFoldersToDelete");
        }

        private void FindFoldersToDeleteWorkerThread(object obj)
        {
            FolderViewModel f = obj as FolderViewModel;
            FindFoldersToDelete(f);
        }
    }

    public enum Response
    {
        None = 0,
        Yes = 1,
        No = 2,
        CapitalNo = 3,
        CapitalYes = 4
    }

    [Flags]
    public enum Options
    {
        None = 0,
        PerformDeletesBasedOnFileSize = 1,
        DoublePass = 2,
        ContinueOnException = 4,
        DeletePSESS = 8,
        RemoveReadOnlyFlags = 16,
        DeleteSVN = 32
    }

    public class FileWorkerUtil
    {
        public event EventHandler<ObjectEventArgs<DirectoryInfo>> DeletedDirectory;
        public event EventHandler<ObjectEventArgs<FileInfo>> DeletedFile;

        public List<string> DefaultIgnoreList;

        public FileWorkerUtil()
        {
            DefaultIgnoreList = new List<string>()
            {
                "System Volume",               
                "Documents and",                
                "Program Files",                
                "ProgramData",                
                "Windows",                                                
            };

            //if (Properties.Settings.Default.IgnoreCUsers)
            //    DefaultIgnoreList.Add("c:\\Users");
        }

        public static bool SetFileAttribute(DirectoryInfo source, FileAttributes setAttribute)
        {
            foreach (FileInfo fi in source.GetFiles())
            {
                if (fi.FullName.IndexOf("A525", StringComparison.OrdinalIgnoreCase) >= 0)
                    return false;

                File.SetAttributes(fi.FullName, setAttribute);
            }

            foreach (DirectoryInfo diSourceSubDir in source.GetDirectories())
            {
                if (diSourceSubDir.FullName.Equals(source.FullName)) continue;

                SetFileAttribute(diSourceSubDir, setAttribute);
            }

            return true;
        }

        public void StartNukeBinObjs(string startingDir, double minSize, double maxSize, Options options, Func<FileInfo, Response> ConfirmDelete, Func<DirectoryInfo, Response> ConfirmDeleteDirectory, Action<Exception> OnException)
        {
            List<Exception> exceptionsOut = new List<Exception>();
            StartNukeBinObjs(new List<string>() { startingDir }, null, exceptionsOut, minSize, maxSize, options, ConfirmDelete, ConfirmDeleteDirectory, OnException);
        }

        public void StartNukeBinObjs(List<string> startingDirs, double minSize, double maxSize, Options options, Func<FileInfo, Response> ConfirmDelete, Func<DirectoryInfo, Response> ConfirmDeleteDirectory, Action<Exception> OnException)
        {
            List<Exception> exceptionsOut = new List<Exception>();
            StartNukeBinObjs(startingDirs, null, exceptionsOut, minSize, maxSize, options, ConfirmDelete, ConfirmDeleteDirectory, OnException);
        }

        public void StartNukeBinObjs(string startingDir, List<string> ignoreList, double minSize, double maxSize, Options options, Func<FileInfo, Response> ConfirmDelete, Func<DirectoryInfo, Response> ConfirmDeleteDirectory, Action<Exception> OnException)
        {
            List<Exception> exceptionsOut = new List<Exception>();
            StartNukeBinObjs(new List<string>() { startingDir }, ignoreList, exceptionsOut, minSize, maxSize, options, ConfirmDelete, ConfirmDeleteDirectory, OnException);
        }

        public void StartNukeBinObjs(List<string> startingDirs, List<string> ignoreList, double minSize, double maxSize, Options options, Func<FileInfo, Response> ConfirmDelete, Func<DirectoryInfo, Response> ConfirmDeleteDirectory, Action<Exception> OnException)
        {
            List<Exception> exceptionsOut = new List<Exception>();
            StartNukeBinObjs(startingDirs, ignoreList, exceptionsOut, minSize, maxSize, options, ConfirmDelete, ConfirmDeleteDirectory, OnException);
        }

        public void StartNukeBinObjs(string startingDir, List<string> ignoreList, List<Exception> exceptionsOut, double minSize, double maxSize, Options options, Func<FileInfo, Response> ConfirmDelete, Func<DirectoryInfo, Response> ConfirmDeleteDirectory, Action<Exception> OnException)
        {
            StartNukeBinObjs(new List<string>() { startingDir }, ignoreList, exceptionsOut, minSize, maxSize, options, ConfirmDelete, ConfirmDeleteDirectory, OnException);
        }

        public void StartNukeBinObjs(List<string> startingDirs, List<string> ignoreList, List<Exception> exceptionsOut, double minSize, double maxSize, Options options, Func<FileInfo, Response> ConfirmDelete, Func<DirectoryInfo, Response> ConfirmDeleteDirectory, Action<Exception> OnException)
        {
            if ((options & Options.DoublePass) == Options.DoublePass)
            {
                StartNukeBinObjsInternal(startingDirs, ignoreList, exceptionsOut, minSize, maxSize, options, ConfirmDelete, ConfirmDeleteDirectory, OnException);
                Options options2 = options | Options.PerformDeletesBasedOnFileSize;
                StartNukeBinObjsInternal(startingDirs, ignoreList, exceptionsOut, minSize, maxSize, options2, ConfirmDelete, ConfirmDeleteDirectory, OnException);
            }
            else
            {
                StartNukeBinObjsInternal(startingDirs, ignoreList, exceptionsOut, minSize, maxSize, options, ConfirmDelete, ConfirmDeleteDirectory, OnException);
            }
        }

        private void StartNukeBinObjsInternal(List<string> startingDirs, List<string> ignoreList, List<Exception> exceptionsOut, double minSize, double maxSize, Options options, Func<FileInfo, Response> ConfirmDelete, Func<DirectoryInfo, Response> ConfirmDeleteDirectory, Action<Exception> OnException)
        {
            foreach (var dirName in startingDirs)
            {
                try
                {
                    DirectoryInfo di = new DirectoryInfo(dirName);
                    StartNukeBinObjs(di, ignoreList, exceptionsOut, minSize, maxSize, options, ConfirmDelete, ConfirmDeleteDirectory, OnException);
                }
                catch (Exception ex)
                {
                    if ((options & Options.ContinueOnException) != Options.ContinueOnException)
                        throw;

                    exceptionsOut.Add(ex);
                }
            }
        }

        public void StartNukeBinObjs(DirectoryInfo source, List<string> ignoreList, List<Exception> exceptionsOut, double minSize, double maxSize, Options options, Func<FileInfo, Response> ConfirmDelete, Func<DirectoryInfo, Response> ConfirmDeleteDirectory, Action<Exception> OnException)
        {
            List<string> allIgnores = new List<string>(DefaultIgnoreList);

            if (ignoreList != null)
                allIgnores.AddRange(ignoreList);

            NukeBinObjs(source, allIgnores, exceptionsOut, minSize, maxSize, options, ConfirmDelete, ConfirmDeleteDirectory, OnException);
        }

        public void StartNukeBinObjs(DirectoryInfo source, List<string> ignoreList, List<Exception> exceptionsOut, double minSize, double maxSize, Options options, Func<FileInfo, Response> ConfirmDelete, Func<DirectoryInfo, Response> ConfirmDeleteDirectory, Action<Exception> OnException, ref List<DirectoryInfo> collected, ref List<FileInfo> filesCollected)
        {
            List<string> allIgnores = new List<string>(DefaultIgnoreList);

            if (ignoreList != null)
                allIgnores.AddRange(ignoreList);

            NukeBinObjs(source, allIgnores, exceptionsOut, minSize, maxSize, options, Response.CapitalYes, ConfirmDelete, ConfirmDeleteDirectory, OnException, ref collected, ref filesCollected);
        }

        private void NukeBinObjs(DirectoryInfo source, List<string> ignoreList, List<Exception> exceptionsOut, double minSize, double maxSize, Options options, Func<FileInfo, Response> ConfirmDelete, Func<DirectoryInfo, Response> ConfirmDeleteDirectory, Action<Exception> OnException)
        {
            NukeBinObjs(source, ignoreList, exceptionsOut, minSize, maxSize, options, Response.None, ConfirmDelete, ConfirmDeleteDirectory, OnException);
        }

        protected void OnDeletedDirectory(DirectoryInfo di)
        {
            if (DeletedDirectory != null)
                DeletedDirectory(this, new ObjectEventArgs<DirectoryInfo>(di));
        }

        protected void OnDeletedFile(FileInfo fi)
        {
            if (DeletedFile != null)
                DeletedFile(this, new ObjectEventArgs<FileInfo>(fi));
        }

        private Response _prevResponseGlobal = Response.None;

        private void NukeBinObjs(DirectoryInfo source, List<string> ignoreList, List<Exception> exceptionsOut, double minSize, double maxSize, Options options, Response prevResponse, Func<FileInfo, Response> ConfirmDelete, Func<DirectoryInfo, Response> ConfirmDeleteDirectory, Action<Exception> OnException)
        {
            if (source.FullName.ContainsAny(ignoreList))
                return;

            bool foundPropertiesDirectory = false;
            bool foundProjectFile = false;

            // Delete .svn directories, if requested
            if ((options & Options.DeleteSVN) == Options.DeleteSVN)
            {
                try
                {
                    HandleDeleteSVN(source);
                }
                catch (Exception ex)
                {
                    OnException(ex);
                    exceptionsOut.Add(ex);

                    if ((options & Options.ContinueOnException) != Options.ContinueOnException)
                        throw;
                }
            }

            try
            {
                foundPropertiesDirectory = FindPropertiesDirectory(source);
                foundProjectFile = FindProjectFile(source);
            }
            catch (Exception ex)
            {
                OnException(ex);
                exceptionsOut.Add(ex);

                if ((options & Options.ContinueOnException) != Options.ContinueOnException)
                    throw;
            }

            if (((options & Options.DeletePSESS) == Options.DeletePSESS))
            {
                prevResponse = HandleDeletePSESS(source, exceptionsOut, minSize, maxSize, options, prevResponse, ConfirmDelete, OnException);
            }

            if ((options & Options.PerformDeletesBasedOnFileSize) == Options.PerformDeletesBasedOnFileSize)
            {
                DeleteBasedOnFileSize(source, exceptionsOut, minSize, maxSize, options, ConfirmDelete, OnException);
            }

            try
            {
                foreach (DirectoryInfo diSourceSubDir in source.GetDirectories())
                {
                    if (diSourceSubDir.FullName.Equals(source.FullName)) continue;

                    if ((foundProjectFile || foundPropertiesDirectory) && DirectoryShouldBeDeleted(diSourceSubDir))
                    {
                        if (prevResponse == Response.CapitalNo)
                            continue;

                        if (prevResponse != Response.CapitalYes && _prevResponseGlobal != Response.CapitalYes)
                        {
                            Response r = ConfirmDeleteDirectory(diSourceSubDir);

                            if (!HandleResponse(r, ref prevResponse))
                                continue;

                            if (prevResponse == Response.CapitalYes)
                            {
                                _prevResponseGlobal = Response.CapitalYes;
                            }
                        }

                        if (WantRemoveReadOnlyFlags(options) && !SetFileAttribute(diSourceSubDir, FileAttributes.Normal))
                            continue;

                        try
                        {
                            Directory.Delete(diSourceSubDir.FullName, true);
                            OnDeletedDirectory(diSourceSubDir);
                        }
                        catch (Exception ex)
                        {
                            OnException(ex);

                            if ((options & Options.ContinueOnException) != Options.ContinueOnException)
                                throw;

                            exceptionsOut.Add(ex);
                        }
                    }
                    else
                    {
                        NukeBinObjs(diSourceSubDir, ignoreList, exceptionsOut, minSize, maxSize, options, prevResponse, ConfirmDelete, ConfirmDeleteDirectory, OnException);
                    }
                }
            }
            catch (Exception ex)
            {
                OnException(ex);
                exceptionsOut.Add(ex);

                if ((options & Options.ContinueOnException) != Options.ContinueOnException)
                    throw;
            }
        }

        private void NukeBinObjs(DirectoryInfo source, List<string> ignoreList, List<Exception> exceptionsOut, double minSize, double maxSize, Options options, Response prevResponse, Func<FileInfo, Response> ConfirmDelete, Func<DirectoryInfo, Response> ConfirmDeleteDirectory, Action<Exception> OnException, ref List<DirectoryInfo> collected, ref List<FileInfo> filesCollected)
        {
            if (collected == null)
            {
                collected = new List<DirectoryInfo>();
            }

            if (filesCollected == null)
            {
                filesCollected = new List<FileInfo>();
            }

            if (source.FullName.ContainsAny(ignoreList))
                return;

            bool foundPropertiesDirectory = false;
            bool foundProjectFile = false;


            // Delete .svn directories, if requested
            if ((options & Options.DeleteSVN) == Options.DeleteSVN)
            {
                try
                {
                    HandleDeleteSVN(source, ref collected);
                }
                catch (Exception ex)
                {
                    OnException(ex);
                    exceptionsOut.Add(ex);

                    if ((options & Options.ContinueOnException) != Options.ContinueOnException)
                        throw;
                }
            }

            try
            {
                foundPropertiesDirectory = FindPropertiesDirectory(source);
                foundProjectFile = FindProjectFile(source);
            }
            catch (Exception ex)
            {
                OnException(ex);
                exceptionsOut.Add(ex);

                if ((options & Options.ContinueOnException) != Options.ContinueOnException)
                    throw;
            }

            if (((options & Options.DeletePSESS) == Options.DeletePSESS))
            {
                prevResponse = HandleDeletePSESS(source, exceptionsOut, minSize, maxSize, options, prevResponse, ConfirmDelete, OnException, ref filesCollected);
            }

            if ((options & Options.PerformDeletesBasedOnFileSize) == Options.PerformDeletesBasedOnFileSize)
            {
                DeleteBasedOnFileSize(source, exceptionsOut, minSize, maxSize, options, ConfirmDelete, OnException, ref filesCollected);
            }

            try
            {
                foreach (DirectoryInfo diSourceSubDir in source.GetDirectories())
                {
                    if (diSourceSubDir.FullName.Equals(source.FullName)) continue;

                    if ((foundProjectFile || foundPropertiesDirectory) && DirectoryShouldBeDeleted(diSourceSubDir))
                    {
                        try
                        {
                            collected.Add(diSourceSubDir);
                        }
                        catch (Exception ex)
                        {
                            OnException(ex);

                            if ((options & Options.ContinueOnException) != Options.ContinueOnException)
                                throw;

                            exceptionsOut.Add(ex);
                        }
                    }
                    else
                    {
                        NukeBinObjs(diSourceSubDir, ignoreList, exceptionsOut, minSize, maxSize, options, prevResponse, ConfirmDelete, ConfirmDeleteDirectory, OnException, ref collected, ref filesCollected);
                    }
                }
            }
            catch (Exception ex)
            {
                OnException(ex);
                exceptionsOut.Add(ex);

                if ((options & Options.ContinueOnException) != Options.ContinueOnException)
                    throw;
            }
        }

        private bool WantRemoveReadOnlyFlags(Options options)
        {
            return ((options & Options.RemoveReadOnlyFlags) == Options.RemoveReadOnlyFlags);
        }

        private bool DirectoryShouldBeDeleted(DirectoryInfo di)
        {
            bool delete = (di.Name.IndexOf("Backup", StringComparison.OrdinalIgnoreCase) == 0 ||
                           di.Name.Equals("bin", StringComparison.OrdinalIgnoreCase) ||
                           di.Name.Equals("obj", StringComparison.OrdinalIgnoreCase));

            //if (delete)
            //{
            //    if (!string.IsNullOrEmpty(Properties.Settings.Default.SkipDeleteSubdir) && di.FullName.IndexOf(Properties.Settings.Default.SkipDeleteSubdir, StringComparison.OrdinalIgnoreCase) >= 0)
            //        return false;

            //    if (!string.IsNullOrEmpty(Properties.Settings.Default.SkipDeleteSubdir2) && di.FullName.IndexOf(Properties.Settings.Default.SkipDeleteSubdir2, StringComparison.OrdinalIgnoreCase) >= 0)
            //        return false;
            //}

            return delete;
        }

        private Response HandleDeletePSESS(DirectoryInfo source, List<Exception> exceptionsOut, double minSize, double maxSize, Options options, Response prevResponse, Func<FileInfo, Response> ConfirmDelete, Action<Exception> OnException)
        {
            foreach (FileInfo fi in source.GetFiles())
            {
                if (fi.Extension.IndexOf("psess", StringComparison.OrdinalIgnoreCase) >= 0)
                {
                    if (prevResponse == Response.CapitalNo)
                        continue;

                    if (prevResponse != Response.CapitalYes)
                    {
                        Response r = ConfirmDelete(fi);

                        if (!HandleResponse(r, ref prevResponse))
                            continue;
                    }

                    try
                    {
                        fi.Delete();
                        OnDeletedFile(fi);
                    }
                    catch (Exception e)
                    {
                        OnException(e);
                        exceptionsOut.Add(e);

                        if ((options & Options.ContinueOnException) != Options.ContinueOnException)
                            throw;
                    }
                }
            }
            return prevResponse;
        }

        private Response HandleDeletePSESS(DirectoryInfo source, List<Exception> exceptionsOut, double minSize, double maxSize, Options options, Response prevResponse, Func<FileInfo, Response> ConfirmDelete, Action<Exception> OnException, ref List<FileInfo> collected)
        {
            if (collected == null)
            {
                collected = new List<FileInfo>();
            }

            foreach (FileInfo fi in source.GetFiles())
            {
                if (fi.Extension.IndexOf("psess", StringComparison.OrdinalIgnoreCase) >= 0)
                {
                    try
                    {
                        collected.Add(fi);
                    }
                    catch (Exception e)
                    {
                        OnException(e);
                        exceptionsOut.Add(e);

                        if ((options & Options.ContinueOnException) != Options.ContinueOnException)
                            throw;
                    }
                }
            }
            return prevResponse;
        }

        private static bool FindPropertiesDirectory(DirectoryInfo source)
        {
            try
            {
                foreach (DirectoryInfo diSourceSubDir in source.GetDirectories())
                {
                    // Make sure we find a "Properties" directory, signaling the directory is a visual studio project dir, etc
                    if (diSourceSubDir.Name.Contains("Properties"))
                    {
                        return true;
                    }
                }
            }
            catch
            {
            }

            return false;
        }

        private void HandleDeleteSVN(DirectoryInfo source)
        {
            foreach (DirectoryInfo diSourceSubDir in source.GetDirectories())
            {
                if (diSourceSubDir.Name.Equals(".svn"))
                {
                    SetFileAttribute(diSourceSubDir, FileAttributes.Normal);
                    diSourceSubDir.Delete(true);
                    OnDeletedDirectory(diSourceSubDir);
                    continue;
                }
            }
        }

        private void HandleDeleteSVN(DirectoryInfo source, ref List<DirectoryInfo> collected)
        {
            if (collected == null)
            {
                collected = new List<DirectoryInfo>();
            }

            foreach (DirectoryInfo diSourceSubDir in source.GetDirectories())
            {
                if (diSourceSubDir.Name.Equals(".svn"))
                {
                    SetFileAttribute(diSourceSubDir, FileAttributes.Normal);
                    collected.Add(diSourceSubDir);
                    continue;
                }
            }
        }

        private static bool FindProjectFile(DirectoryInfo source)
        {
            try
            {
                foreach (FileInfo fi in source.GetFiles())
                {
                    // Make sure there is a project in the parent directory / again, ensure it's a VS project
                    if (fi.Name.IndexOf(".csproj") > 0 || fi.Name.IndexOf(".vbproj") > 0)
                    {
                        return true;
                    }
                }
            }
            catch
            {
            }

            return false;
        }

        private bool HandleResponse(Response r, ref Response prevResponse)
        {
            if (r == Response.CapitalYes)
            {
                prevResponse = Response.CapitalYes;
            }
            else if (r == Response.CapitalNo)
            {
                prevResponse = Response.CapitalNo;
                return false;
            }
            else if (r != Response.Yes)
            {
                return false;
            }

            return true;
        }

        private static bool DeleteBasedOnFileSize(DirectoryInfo source, List<Exception> exceptionsOut, double min, double max, Options options, Func<FileInfo, Response> ConfirmDelete, Action<Exception> OnException)
        {
            if (min == 0D)
                return true;

            Dictionary<string, bool> askedList = new Dictionary<string, bool>();
            bool stopDir = false;

            for (int size = 0; size < 1; size++)
            {
                if (stopDir)
                    break;

                long minSize = Convert.ToInt64(Math.Floor(1024D * 1024D * min));
                long maxSize = (max == 0D) ? long.MaxValue : Convert.ToInt64(Math.Floor(1024D * 1024D * max));

                foreach (FileInfo fi in source.GetFiles())
                {
                    if (fi.Length >= minSize && fi.Length < maxSize)
                    {
                        if (!askedList.ContainsKey(fi.FullName))
                        {
                            askedList.Add(fi.FullName, true);

                            Response r = ConfirmDelete(fi);

                            if (r == Response.CapitalNo)
                            {
                                stopDir = true;
                                break;
                            }
                            else if (r == Response.No)
                            {
                                continue;
                            }

                            try
                            {
                                File.SetAttributes(fi.FullName, FileAttributes.Normal);
                                fi.Delete();
                            }
                            catch (Exception ex)
                            {
                                OnException(ex);

                                if ((options & Options.ContinueOnException) != Options.ContinueOnException)
                                    throw;

                                exceptionsOut.Add(ex);
                            }
                        }
                    }
                }
            }
            return stopDir;
        }

        private static bool DeleteBasedOnFileSize(DirectoryInfo source, List<Exception> exceptionsOut, double min, double max, Options options, Func<FileInfo, Response> ConfirmDelete, Action<Exception> OnException, ref List<FileInfo> filesCollected)
        {
            if (filesCollected == null)
            {
                filesCollected = new List<FileInfo>();
            }

            if (min == 0D)
                return true;

            Dictionary<string, bool> askedList = new Dictionary<string, bool>();
            bool stopDir = false;

            for (int size = 0; size < 1; size++)
            {
                if (stopDir)
                    break;

                long minSize = Convert.ToInt64(Math.Floor(1024D * 1024D * min));
                long maxSize = (max == 0D) ? long.MaxValue : Convert.ToInt64(Math.Floor(1024D * 1024D * max));

                foreach (FileInfo fi in source.GetFiles())
                {
                    if (fi.Length >= minSize && fi.Length < maxSize)
                    {
                        try
                        {
                            File.SetAttributes(fi.FullName, FileAttributes.Normal);
                            filesCollected.Add(fi);
                        }
                        catch (Exception ex)
                        {
                            OnException(ex);

                            if ((options & Options.ContinueOnException) != Options.ContinueOnException)
                                throw;

                            exceptionsOut.Add(ex);
                        }
                    }
                }
            }
            return stopDir;
        }
    }

    public static class Ext
    {
        public static bool ContainsAny(this string originalString, IEnumerable<string> list)
        {
            foreach (var item in list)
            {
                if (originalString.IndexOf(item, StringComparison.OrdinalIgnoreCase) >= 0)
                    return true;
            }

            return false;
        }
    }

}
